#include "StrVec.h"

allocator<string> StrVec::alloc;
/**
 * @brief 分配足够的内存来存储从 beg 到 end 范围内的 string 对象，并将这些对象复制到新分配的内存中。
 * 
 * @param beg 指向要复制的 string 对象范围的起始位置的指针。
 * @param end 指向要复制的 string 对象范围的结束位置（不包含该位置的对象）的指针。
 * @return std::pair<std::string*, std::string*> 一个 pair，第一个元素是新分配内存的起始地址，
 * 第二个元素是复制操作结束后的地址（即最后一个复制对象的下一个位置）。
 */
pair<string*, string*> StrVec::alloc_n_copy(const string* beg, const string* end){
    // 调用 alloc（应该是 StrVec 类中的 std::allocator<string> 成员）分配足够的内存
    // 以存储从 beg 到 end 范围内的 string 对象数量
    auto data = alloc.allocate(end - beg);
    // 使用 uninitialized_copy 算法将 [beg, end) 范围内的 string 对象复制到新分配的内存中
    // 返回一个 pair，第一个元素是新分配内存的起始地址，第二个元素是复制操作结束后的地址
    return {data, uninitialized_copy(beg, end, data)};
}

/**
 * @brief 释放 StrVec 类对象所占用的动态内存，并销毁其中存储的所有 string 对象。
 * 
 * 该函数首先检查是否有分配的内存需要释放，如果有，则逆序销毁存储在内存中的每个 string 对象，
 * 最后释放分配的内存块。
 */
void StrVec::free(){
    // 检查 elements 是否为 nullptr，即是否有内存需要释放
    if(elements){
        // 从最后一个元素开始，逆序销毁每个元素
        // first_free 指向最后一个已构造元素的下一个位置
        for(auto p = first_free; p != elements;){
            // 调用 alloc.destroy 销毁当前元素
            // --p 先将指针前移，指向要销毁的元素
            alloc.destroy(--p);
        }  
        // 释放分配的内存
        // elements 是内存块的起始地址
        // cap - elements 计算出分配的内存块的大小（元素数量）
        alloc.deallocate(elements, cap - elements);
    } 
}
//使用for_each和lambda表达式实现
#if 0
**
 * @brief 释放 StrVec 类对象所占用的动态内存，并销毁其中存储的所有 string 对象。
 * 
 * 该函数首先检查是否有分配的内存需要释放，如果有，则逆序销毁存储在内存中的每个 string 对象，
 * 最后释放分配的内存块。
 */
void StrVec::free(){
    // 检查 elements 是否为 nullptr，即是否有内存需要释放
    if(elements){
        // 使用 std::for_each 和 lambda 表达式逆序销毁元素
        std::for_each(std::make_reverse_iterator(first_free), std::make_reverse_iterator(elements),
            [this](std::string& elem) {
                alloc.destroy(&elem);
            });
        
        // 释放分配的内存
        // elements 是内存块的起始地址
        // cap - elements 计算出分配的内存块的大小（元素数量）
        alloc.deallocate(elements, cap - elements);
    } 
}
#endif

/**
 * @brief 重新分配 StrVec 类对象的内存空间，以容纳更多元素。
 * 
 * 该函数会计算新的内存容量，分配新的内存块，将原有的元素移动到新内存块中，
 * 然后释放旧的内存块，最后更新相关指针以指向新的内存块。
 */
void StrVec::reallocate(){
    // 计算新的容量
    // 如果当前容量为 0，则新容量为1
    // 否则，新容量为当前容量的两倍
    auto newcapacity = size() ? 2 * size() : 1;
    // 分配新的内存块
    // 调用 alloc.allocate 分配 newcapacity 个 string 对象所需的内存
    auto newdata = alloc.allocate(newcapacity);
    // 移动数据到新内存块
    // 初始化目标指针，指向新内存块的起始位置
    auto dest = newdata;
    // 初始化源指针，指向旧内存块的起始位置
    auto elem = elements;
    // 遍历旧内存块中的所有元素
    for(size_t i = 0; i != size(); ++i){
        // 调用 alloc.construct 在新内存块中构造当前元素
        // 第一个参数是要构造的元素的地址，第二个参数是要移动的元素的值
        // 使用 std::move 进行移动语义，避免不必要的复制
        alloc.construct(dest++, std::move(*elem++)); 
    } 
    // 释放旧内存块
    free();
    // 更新 elements 指向新内存块的起始位置
    elements = newdata;
    // 更新 first_free 指向新内存块中最后一个已构造元素的下一个位置
    first_free = dest;
    // 更新 cap 指向新内存块的结束位置
    cap = elements + newcapacity;
}

StrVec::StrVec(const initializer_list<string>& il){
    // 调用 alloc_n_copy 函数，分配足够的内存并将 il 中的元素复制到新分配的内存中
    // data 是一个 std::pair<std::string*, std::string*>，first 为新内存起始地址，second 为复制结束地址
    auto data = alloc_n_copy(il.begin(), il.end()); 
    // 更新 elements 指针，使其指向新分配内存的起始位置
    elements = data.first;
    // 更新 first_free 和 cap 指针，使其指向新分配内存中最后一个已构造元素的下一个位置
    first_free = cap = data.second;
}
/**
 * @brief StrVec 类的拷贝构造函数，用于创建一个新的 StrVec 对象，该对象是另一个 StrVec 对象的副本。
 * 
 * @param s 要复制的 StrVec 对象的引用。
 */
StrVec::StrVec(const StrVec& s){
    // 调用 alloc_n_copy 成员函数，该函数会分配足够的内存
    // 并将传入对象 s 中从开始到结束的元素复制到新分配的内存中
    // 注意：代码中存在拼写错误，s.bengin() 应改为 s.begin()
    auto newdata = alloc_n_copy(s.begin(), s.end());
    // 更新 elements 指针，使其指向新分配的内存块的起始位置
    elements = newdata.first;
    // 更新 first_free 和 cap 指针，使其指向新分配的内存块中最后一个已构造元素的下一个位置
    first_free = cap = newdata.second; 
}

/**
 * @brief StrVec 类的拷贝赋值运算符重载函数，用于将一个 StrVec 对象赋值给另一个 StrVec 对象。
 * 
 * 该函数会先分配足够的内存来复制右侧对象（rhs）的元素，然后释放当前对象原有的内存，
 * 最后更新当前对象的指针以指向新分配的内存。同时会检查自赋值情况，避免不必要的操作。
 * 
 * @param rhs 要赋值的 StrVec 对象的引用。
 * @return StrVec& 返回当前对象的引用，以支持链式赋值操作。
 */
StrVec& StrVec::operator=(const StrVec& rhs){
    // 注意：代码中存在拼写错误，rhs.bengin() 应改为 rhs.begin()
    // 调用 alloc_n_copy 函数，分配足够的内存并将 rhs 中的元素复制到新分配的内存中
    // data 是一个 std::pair<std::string*, std::string*>，first 为新内存起始地址，second 为复制结束地址
    auto data = alloc_n_copy(rhs.begin(), rhs.end());
    // 释放当前对象原有的内存，并销毁其中的元素
    free();
    // 更新当前对象的 elements 指针，使其指向新分配内存的起始位置
    elements = data.first;
    // 更新 first_free 和 cap 指针，使其指向新分配内存中最后一个已构造元素的下一个位置
    first_free = cap = data.second;
    // 返回当前对象的引用，支持链式赋值
    return *this; 
}

StrVec::~StrVec(){
    // 调用 free 函数释放当前对象所占用的内存
    free();
}

void StrVec::push_back(const string& s){
    // 检查当前内存是否已满，如果已满则重新分配内存
    chk_n_alloc();
    // 在 first_free 指向的位置构造一个新的 string 对象，其值为 s
    alloc.construct(first_free++, s);  
}

/**
 * @brief 为 StrVec 类对象预留指定容量的内存空间。
 * 
 * 如果指定的新容量大于当前容量，该函数会分配一块新的内存空间，
 * 将原有的元素移动到新的内存空间中，然后释放旧的内存空间，
 * 最后更新相关指针以指向新的内存空间。如果新容量小于等于当前容量，
 * 则不进行任何操作直接返回。
 * 
 * @param new_cap 要预留的新的内存容量。
 */
void StrVec::reserve(size_t new_cap) {
    // 检查新容量是否小于等于当前容量，如果是则直接返回，不进行任何操作
    if (new_cap <= capacity()) return;

    // 分配新的内存块
    // 调用 alloc.allocate 方法分配 new_cap 个 string 对象所需的内存空间
    // 并将新内存块的起始地址存储在 newdata 中
    auto newdata = alloc.allocate(new_cap);

    // 移动数据到新内存块
    // 初始化目标指针 dest，指向新内存块的起始位置
    auto dest = newdata;
    // 初始化源指针 elem，指向旧内存块的起始位置
    auto elem = elements;
    // 遍历旧内存块中的所有元素
    for (size_t i = 0; i != size(); ++i) {
        // 调用 alloc.construct 在新内存块中构造当前元素
        // 使用 std::move 进行移动语义，避免不必要的复制
        // 第一个参数是要构造的元素的地址，第二个参数是要移动的元素的值
        alloc.construct(dest++, std::move(*elem++));
    }

    // 释放旧内存块
    // 调用 free 函数销毁旧内存块中的元素并释放其占用的内存
    free();

    // 更新指针
    // 更新 elements 指针，使其指向新内存块的起始位置
    elements = newdata;
    // 更新 first_free 指针，使其指向新内存块中最后一个已构造元素的下一个位置
    first_free = dest;
    // 更新 cap 指针，使其指向新内存块的结束位置
    cap = elements + new_cap;
}

/**
 * @brief 调整 StrVec 容器的大小为指定的元素数量。
 * 
 * 该函数是 `resize` 函数的重载版本，它会调用另一个 `resize` 重载版本，
 * 并将新元素的默认值设置为空字符串。当需要将容器大小调整为 `count` 时，
 * 若新的大小大于当前大小，新添加的元素会被初始化为空字符串；
 * 若新的大小小于当前大小，多余的元素会被销毁。
 * 
 * @param count 调整后容器的目标元素数量。
 */
void StrVec::resize(size_t count) {
    // 调用另一个重载的 resize 函数，将新元素的默认值设置为空字符串
    resize(count, std::string());
}

/**
 * @brief 调整 StrVec 容器的大小为指定的元素数量。
 * 
 * 若指定的数量 `count` 大于当前容器的大小，会在容器末尾添加新元素，新元素的值为 `value`。
 * 若需要，会先调用 `reserve` 函数分配足够的内存。
 * 若指定的数量 `count` 小于当前容器的大小，会从容器末尾开始销毁多余的元素。
 * 
 * @param count 调整后容器的目标元素数量。
 * @param value 当容器需要扩容时，新添加元素的默认值。
 */
void StrVec::resize(size_t count, const std::string& value) {
    // 检查指定的数量是否大于当前容器的大小
    if (count > size()) {
        // 若指定的数量大于当前容器的容量，需要先分配更多的内存
        if (count > capacity()) {
            // 调用 reserve 函数预留内存，取 count 和当前容量两倍中的较大值
            reserve(std::max(count, 2 * capacity()));
        }
        // 从当前容器的末尾开始，依次构造新元素，直到达到指定的数量
        for (size_t i = size(); i < count; ++i) {
            // 在 first_free 指向的位置构造一个新元素，值为 value
            // 构造完成后，first_free 指针向后移动一位
            alloc.construct(first_free++, value);
        }
    } 
    // 检查指定的数量是否小于当前容器的大小
    else if (count < size()) {
        // 从容器的末尾开始，依次销毁多余的元素，直到容器大小等于指定的数量
        while (first_free != elements + count) {
            // 先将 first_free 指针向前移动一位，指向要销毁的元素
            // 然后调用 alloc.destroy 函数销毁该元素
            alloc.destroy(--first_free);
        }
    }
}